package LiuYun

import spinal.core._
import spinal.lib._

class StageEx3 extends Component{
    val io = new Bundle{
        val ic    :PipeCtrl = slave(new PipeCtrl)
        val idat  :SDatEx3  = in  (new SDatEx3)
        val oc    :PipeCtrl = master(new PipeCtrl)
        val odat  :SDatWb   = out (new SDatWb)
        val div   :DivO      = slave(new DivO(32))
        val dresp :DataResp   = slave(new DataResp)
        val fw    :Forwarding = out(new Forwarding)
        val exbus :CoreCancel = in  (new CoreCancel)
    }
    val cancel:Bool = io.exbus.cancel
    val data:SDatWb = new SDatWb assignedFrom io.idat
    val pipe = new StageBuffer[SDatWb](data)
    pipe.update(io.ic,io.oc,pipe.ex)
    io.odat := pipe.data
    val I = Instructions
    val op:UInt = io.idat.subcode
    val valid_ok:Bool = io.ic.valid && !io.ic.ex

    io.dresp.cancel := cancel

    io.fw.valid := io.ic.valid
    io.fw.grwr  := data.grwr
    io.fw.dest  := data.dest
    io.fw.value := data.value

    val mem = new Area{
        val valid:Bool = valid_ok && io.idat.itype.is_mem
        val ld:Bool = valid && I.isSubOpCode(op,"mem","ld")
        val st:Bool = valid && I.isSubOpCode(op,"mem","st")
        val va:UInt = io.idat.va
        val ld_is_b:Bool = valid && I.isSubOpCode(op,"mem.ld","b")
        val ld_is_h:Bool = valid && I.isSubOpCode(op,"mem.ld","h")
        val ld_is_w:Bool = valid && I.isSubOpCode(op,"mem.ld","w")
        val rd:UInt = io.dresp.data
        val rd_s:UInt = rd |>> (va(1 downto 0) << 3)
        val rd_bu:UInt = rd_s(0, 8 bits)       .resize(32)
        val rd_hu:UInt = rd_s(0,16 bits)       .resize(32)
        val rd_bs:UInt = rd_s(0, 8 bits).asSInt.resize(32).asUInt
        val rd_hs:UInt = rd_s(0,16 bits).asSInt.resize(32).asUInt
        val sign :Bool = io.idat.itype.sign
        val block:Bool = (st || ld) && !io.dresp.valid
        val res  :UInt = ( Mux(ld_is_b,Mux(sign,rd_bs,rd_bu),U(0))
                         | Mux(ld_is_h,Mux(sign,rd_hs,rd_hu),U(0))
                         | Mux(ld_is_w,         rd          ,U(0)))
        when(ld){
            data.value := res
        }
        io.dresp.allow := valid
    }
    val div = new Area{
        val valid:Bool = valid_ok && io.idat.itype.is_div
        val block:Bool = valid && !io.div.valid
        val res :UInt = I.select(op,"d/m",Map(
            "div"->io.div.q,
            "mod"->io.div.r)
        )
        when(valid){
            data.value := res
        }
        io.div.allow := valid
    }
    io.fw.block := mem.block || div.block
    pipe.ready := !io.fw.block
    pipe.newex := False
}
